﻿using System;
using System.Windows.Media;

namespace Js.Service
{
    /// <summary>
    /// 무작위 데이터 생성하는 클래스입니다.
    /// </summary>
    public class RandomDataGeneratorService
    {
        #region Instance For Singleton

        private static RandomDataGeneratorService _Instance;
        private static readonly object _Lock = new object();

        #endregion Instance For Singleton

        #region Member Instance

        private Random _random;

        #endregion Member Instance

        #region Constructor

        /// <summary>
        /// RandomDataGeneratorService 클래스의 새 인스턴스를 초기화합니다.
        /// </summary>
        private RandomDataGeneratorService()
        {
            _random = new Random();
        }

        /// <summary>
        /// RandomDataGeneratorService 클래스의 인스턴스를 생성합니다.
        /// </summary>
        /// <returns>RandomDataGeneratorService 클래스의 인스턴스입니다.</returns>
        public static RandomDataGeneratorService Create()
        {
            lock(_Lock)
            {
                if(_Instance == null)
                {
                    _Instance = new RandomDataGeneratorService();
                }
            }
            return _Instance;
        }

        #endregion Constructor

        #region Method

        /// <summary>
        /// True 또는 False를 반환합니다.
        /// </summary>
        /// <returns>True 또는 False가 반환됩니다.</returns>
        public bool GetBoolean()
        {
            return _random.Next(0, 1) > 0;
        }

        /// <summary>
        /// 무작위 문자 데이터를 반환합니다.
        /// </summary>
        /// <returns>문자 데이터가 반환됩니다.</returns>
        public char GetCharacter()
        {
            string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-=`~!@#$%^&*()_+[]{};':\"\\|,./<>?";
            return characters[_random.Next(characters.Length)];
        }

        /// <summary>
        /// 무작위 Color 인스턴스를 반환합니다.
        /// </summary>
        /// <returns>무작위 Color 인스턴스가 반환됩니다.</returns>
        public Color GetColor()
        {
            return Color.FromRgb((byte)_random.Next(256), (byte)_random.Next(256), (byte)_random.Next(256));
        }

        /// <summary>
        /// 무작위 날짜와 시간 데이터를 반환합니다.
        /// </summary>
        /// <returns>1900년 1월 1일 0시 0분 0초보다 크고 현재 날짜와 시간보다 작은
        /// 날짜와 시간을 가지는 데이터가 반환됩니다.</returns>
        public DateTime GetDateTime()
        {
            return GetDateTime(new DateTime(1900, 1, 1, 0, 0, 0), DateTime.Now);
        }

        /// <summary>
        /// 지정된 범위 내의 무작위 날짜와 시간 데이터를 반환합니다.
        /// </summary>
        /// <param name="from">생성될 날짜와 시간 데이터의 하한입니다.</param>
        /// <param name="to">생성될 날짜와 시간 데이터의 상한입니다.</param>
        /// <returns>from보다 크거나 같고 to보다 작은 날짜와 시간을 가지는 데이터가 반환됩니다.</returns>
        public DateTime GetDateTime(DateTime from, DateTime to)
        {
            TimeSpan range = new TimeSpan(to.Ticks - from.Ticks);
            return from + new TimeSpan((long)(range.Ticks * _random.NextDouble()));
        }

        /// <summary>
        /// 0.0과 1.0 사이의 난수를 반환합니다.
        /// </summary>
        /// <returns>0.0보다 크거나 같고 1.0보다 작은 배정밀도 부동 소수점 숫자가 반환됩니다.</returns>
        public double GetDouble()
        {
            return _random.NextDouble();
        }

        /// <summary>
        /// 지정된 최대값보다 작은 음수가 아닌 소수점을 가지는 난수를 반환합니다.
        /// </summary>
        /// <param name="max">생성될 난수의 상한(포함)입니다. maxValue는 0보다 크거나 같아야 합니다.</param>
        /// <param name="digit">반환 값의 소수 자릿수입니다.</param>
        /// <returns>지정된 최대값보다 작은 음수가 아닌 배정밀도 부동 소수점을 가지는 숫자가 반환됩니다.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException">maxValue가 0보다 작습니다.</exception>
        public double GetDouble(int max, int digit)
        {
            return Math.Round(_random.Next(max) + _random.NextDouble(), digit);
        }

        /// <summary>
        /// 지정된 범위 내의 소수점을 가지는 난수를 반환합니다.
        /// </summary>
        /// <param name="min">반환되는 난수의 하한(포함)입니다.</param>
        /// <param name="max">반환되는 난수의 상한(포함)입니다.maxValue는 minValue보다 크거나 같아야 합니다.</param>
        /// <param name="digit">반환 값의 소수 자릿수입니다.</param>
        /// <returns>minValue보다 크거나 같고 maxValue보다 작은 배정밀도 부동 소수점을 가지는 숫자가 반환됩니다.
        /// minValue가 maxValue와 같은 경우에는 minValue가 반환됩니다.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException">minValue가 maxValue보다 큰 경우</exception>
        public double GetDouble(int min, int max, int digit)
        {
            return Math.Round(_random.Next(min, max) + _random.NextDouble(), digit);
        }

        /// <summary>
        /// 음수가 아닌 난수 데이터를 반환합니다.
        /// </summary>
        /// <returns>0보다 크거나 같고 System.Int32.MaxValue보다 작은 부호 있는 32비트 정수가 반환됩니다.</returns>
        public int GetInt()
        {
            return _random.Next();
        }

        /// <summary>
        /// 지정된 최대값보다 작은 음수가 아닌 난수를 반환합니다.
        /// </summary>
        /// <param name="max">생성될 난수의 상한(포함)입니다. maxValue는 0보다 크거나 같아야 합니다.</param>
        /// <returns>0보다 크거나 같고 maxValue보다 작은 부호 있는 32비트 정수가 반환됩니다.
        /// maxValue가 0과 같으면 0이 반환됩니다.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException">maxValue가 0보다 작습니다.</exception>
        public int GetInt(int max)
        {
            return _random.Next(max == Int32.MaxValue ? max : max + 1);
        }

        /// <summary>
        /// 지정된 범위 내의 난수를 반환합니다.
        /// </summary>
        /// <param name="min">반환되는 난수의 하한(포함)입니다.</param>
        /// <param name="max">반환되는 난수의 상한(포함)입니다.maxValue는 minValue보다 크거나 같아야 합니다.</param>
        /// <returns>minValue보다 크거나 같고 maxValue보다 작은 부호 있는 32비트 정수가 반환됩니다.
        /// minValue가 maxValue와 같은 경우에는 minValue가 반환됩니다.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException">minValue가 maxValue보다 큰 경우</exception>
        public int GetInt(int min, int max)
        {
            return _random.Next(min, max == Int32.MaxValue ? max : max + 1);
        }

        /// <summary>
        /// a-z 사이의 소문자를 반환합니다.
        /// </summary>
        /// <returns>a-z 사이의 소문자가 반환됩니다.</returns>
        public char GetLowerCase()
        {
            string lowerCase = "abcdefghijklmnopqrstuvwxyz";
            return lowerCase[_random.Next(lowerCase.Length)];
        }

        /// <summary>
        /// A-Z 사이의 대문자를 반환합니다.
        /// </summary>
        /// <returns>A-Z 사이의 대문자가 반환됩니다.</returns>
        public char GetUpperCase()
        {
            string upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            return upperCase[_random.Next(upperCase.Length)];
        }

        /// <summary>
        /// 사람 이름을 반환합니다.
        /// </summary>
        /// <returns>사람 이름이 반환됩니다.</returns>
        public string GetName()
        {
            return GetName(GetBoolean() ? Sex.Male : Sex.Female);
        }

        /// <summary>
        /// 지정된 성별의 사람 이름을 반환합니다.
        /// </summary>
        /// <param name="sex">이름의 성별입니다.</param>
        /// <returns>지정된 성별의 사람 이름이 반환됩니다.</returns>
        public string GetName(Sex sex)
        {
            string[][] names = new string[][]
            {
                new string[] {
                    "James", "John", "Robert", "Michael", "William", "David", "Richard", "Charles", "Joseph", "Thomas",
                    "Christopher", "Daniel", "Paul", "Mark", "Donald", "George", "Kenneth", "Steven", "Edward", "Brian",
                    "Ronald", "Anthony", "Kevin", "Jason", "Matthew", "Gary", "Timothy", "Jose", "Larry", "Jeffrey",
                    "Frank", "Scott", "Eric", "Stephen", "Andrew", "Raymond", "Gregory", "Joshua", "Jerry", "Dennis",
                    "Walter", "Patrick", "Peter", "Harold", "Douglas", "Henry", "Carl", "Arthur", "Ryan", "Roger",
                    "Joe", "Juan", "Jack", "Albert", "Jonathan", "Justin", "Terry", "Gerald", "Keith", "Samuel",
                    "Willie", "Ralph", "Lawrence", "Nicholas", "Roy", "Benjamin", "Bruce", "Brandon", "Adam", "Harry",
                    "Fred", "Wayne", "Billy", "Steve", "Louis", "Jeremy", "Aaron", "Randy", "Howard", "Eugene",
                    "Carlos", "Russell", "Bobby", "Victor", "Martin", "Ernest", "Phillip", "Todd", "Jesse", "Craig",
                    "Alan", "Shawn", "Clarence", "Sean", "Philip", "Chris", "Johnny", "Earl", "Jimmy", "Antonio"
                },
                new string[] {
                    "Mary", "Patricia", "Linda", "Barbara", "Elizabeth", "Jennifer", "Maria", "Susan", "Margaret", "Dorothy",
                    "Lisa", "Nancy", "Karen", "Betty", "Helen", "Sandra", "Donna", "Carol", "Ruth", "Sharon",
                    "Michelle", "Laura", "Sarah", "Kimberly", "Deborah", "Jessica", "Shirley", "Cynthia", "Angela", "Melissa",
                    "Brenda", "Amy", "Anna", "Rebecca", "Virginia", "Kathleen", "Pamela", "Martha", "Debra", "Amanda",
                    "Stephanie", "Carolyn", "Christine", "Marie", "Janet", "Catherine", "Frances", "Ann", "Joyce", "Diane",
                    "Alice", "Julie", "Heather", "Teresa", "Doris", "Gloria", "Evelyn", "Jean", "Cheryl", "Mildred",
                    "Katherine", "Joan", "Ashley", "Judith", "Rose", "Janice", "Kelly", "Nicole", "Judy", "Christina",
                    "Kathy", "Theresa", "Beverly", "Denise", "Tammy", "Irene", "Jane", "Lori", "Rachel", "Marilyn",
                    "Andrea", "Kathryn", "Louise", "Sara", "Anne", "Jacqueline", "Wanda", "Bonnie", "Julia", "Ruby",
                    "Lois", "Tina", "Phyllis", "Norma", "Paula", "Diana", "Annie", "Lillian", "Emily", "Robin"
                }
            };
            string[] surnames = new string[]
            {
                "Smith", "Johnson", "Williams", "Brown", "Jones", "Miller", "Davis", "Garcia", "Rodriguez", "Wilson",
                "Martinez", "Anderson", "Taylor", "Thomas", "Hernandez", "Moore", "Martin", "Jackson", "Thompson", "White",
                "Lopez", "Lee", "Gonzalez", "Harris", "Clark", "Lewis", "Robinson", "Walker", "Perez", "Hall",
                "Young", "Allen", "Sanchez", "Wright", "King", "Scott", "Green", "Baker", "Adams", "Nelson",
                "Hill", "Ramirez", "Campbell", "Mitchell", "Roberts", "Carter", "Phillips", "Evans", "Turner", "Torres",
                "Parker", "Collins", "Edwards", "Stewart", "Flores", "Morris", "Nguyen", "Murphy", "Rivera", "Cook",
                "Rogers", "Morgan", "Peterson", "Cooper", "Reed", "Bailey", "Bell", "Gomez", "Kelly", "Howard",
                "Ward", "Cox", "Diaz", "Richardson", "Wood", "Watson", "Brooks", "Bennett", "Gray", "James",
                "Reyes", "Cruz", "Hughes", "Price", "Myers", "Long", "Foster", "Sanders", "Ross", "Morales",
                "Powell", "Sullivan", "Russell", "Ortiz", "Jenkins", "Gutierrez", "Perry", "Butler", "Barnes", "Fisher"
            };

            return String.Format("{0} {1}", names[(int)sex][_random.Next(names[(int)sex].Length)], surnames[_random.Next(surnames.Length)]);
        }

        #endregion Method
    }
}